package com.hillstone.app.aspect;


import com.hillstone.app.anno.RedisLock;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.concurrent.TimeUnit;
/**
 * @author dengliu
 */
@Aspect
@Component
public class RedisLockAspect {
    @Autowired
    private RedissonClient redissonClient;
    @Pointcut("@annotation(com.hillstone.app.anno.RedisLock)")
    public void pointcut() {

    }

    @Around("pointcut() && @annotation(redisLock)")
    public Object around(ProceedingJoinPoint pjp, RedisLock redisLock) throws Throwable {
        String lockName = redisLock.lockName();
        long releaseTime = redisLock.releaseTime();
        long tryLockTime = redisLock.tryLockTime();
        TimeUnit timeUnit = redisLock.timeUnit();

        String key = lockNameResolver(pjp, lockName);
        RLock rLock = redissonClient.getLock(key);
        if (tryLockTime < 0) {
            rLock.lock(releaseTime, timeUnit);
        } else if (!rLock.tryLock(tryLockTime, timeUnit)) {
            throw new RuntimeException("重试后，未拿到锁");
        }
        try {
            return pjp.proceed();
        } finally {
            // 判断是当前线程加的锁
            if (rLock.isHeldByCurrentThread()) {
                rLock.unlock();
            }
        }
    }

    /**
     * 对模板中的占位符替换，得到Redis锁真正的key
     *
     * @param joinPoint
     * @param keyTemplate
     * @return
     */
    public String lockNameResolver(JoinPoint joinPoint, String keyTemplate) {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        String[] names = methodSignature.getParameterNames();
        Object[] args = joinPoint.getArgs();
        HashMap<String, Object> params = new HashMap<>();
        for (int i = 0; i < names.length; i++) {
            params.put(names[i], args[i]);
        }
        ExpressionParser parser = new SpelExpressionParser();
        TemplateParserContext parserContext = new TemplateParserContext();
        String result = parser.parseExpression(keyTemplate, parserContext).getValue(params, String.class);
        return result;
    }
}
